package org.hawk.gwt.ppc.javacc;

import java.io.IOException;
import java.io.Writer;

import org.hawk.gwt.ppc.Log;
import org.hawk.gwt.ppc.scanner.TokenChain;

class TokenChainIterator implements TokenChain<Token> {
	private Token currentToken;
	private int commitIndex = 0;
	private Writer output;
	
	TokenChainIterator(Token currentToken, Writer output) {
		if(currentToken==null){
			throw new NullPointerException();
		}
		this.currentToken = currentToken;
		this.output = output;
	}

	public Token current() {
		return currentToken;
	}

	public boolean hasNext() {
		return currentToken.next!=null;
	}

	public Token next() {
		if(currentToken.next==null){
			throw new IndexOutOfBoundsException();
		}
		return currentToken = currentToken.next;
	}

	public boolean hasPrevious() {
		return currentToken.previous!=null;
	}

	public Token previous() {
		if(currentToken.previous==null){
			throw new IndexOutOfBoundsException();
		}
		return currentToken = currentToken.previous;
	}

	public int nextIndex() {
		if(currentToken.next==null)return -1;
		return currentToken.next.index;
	}

	public int previousIndex() {
		if(currentToken.previous==null)return -1;
		return currentToken.previous.index;
	}

	public int index() {
		return currentToken==null?-1:currentToken.index;
	}

	public void moveTo(int index) throws IndexOutOfBoundsException {
		if(currentToken.index<index)moveForward(index);
		else moveBackward(index);
		
		checkIndex(index);
	}
	
	void toLastRead(){
		while(currentToken.next!=null){
			currentToken = currentToken.next;
		}
	}

	private void checkIndex(int index) {
		if(currentToken.index!=index){
			throw new IndexOutOfBoundsException("Iterated to "+
				currentToken.index+" while moving to "+index+
				" and has no more elements to move");
		}
	}

	private void moveBackward(int index) {
		while(hasPrevious()&&
			currentToken.index>index){
			previous();
		}
	}

	private void moveForward(int index) {
		while(hasNext()&&
			currentToken.index<index){
			next();
		}
	}

	public void remove() {
		Token prev = currentToken.previous;
		Token next = currentToken.next;
		if(prev!=null)prev.next = next;
		if(next!=null)next.previous = prev;
		currentToken = prev==null?next:prev;
	}

	public void set(Token e) {
		Token prev = currentToken.previous;
		Token next = currentToken.next;
		if(prev!=null)prev.next = e;
		if(next!=null)next.previous = e;
		e.next = next;
		e.previous = prev;
		currentToken = e;
	}

	public void add(Token e) {
		currentToken.added.append(e.raw);
	}

	public void add(String token) {
		currentToken.added.append(token);
	}

	public void commit() {
		if(currentToken.index<commitIndex){
			return;
		}
		Token begin = currentToken;
		while(begin.previous!=null||begin.index>commitIndex){
			begin = begin.previous;
		}
		commit(begin);
		skip();
	}

	private void commit(Token begin) {
		if(output==null)return;
		try {
			do{
				if(begin.raw==null){
					begin.raw = "";
				}
				output.write(begin.raw.toString());
				output.write(begin.added.toString());
				begin = begin.next;
			}
			while(begin!=null&&currentToken.index>=begin.index);
			output.flush();
		}
		catch(IOException ex){
			Log.error("Error writing file", ex);
		}
	}

	public void skip() {
		if(currentToken.index<commitIndex){
			return;
		}
		commitIndex = currentToken.index+1;
	}

	@Override
	public String toString() {
		Token begin = currentToken;
		while(begin.previous!=null)begin = begin.previous;
		StringBuilder result = new StringBuilder();
		while(begin.index<=currentToken.index){
			result.append(begin.raw);
			begin = begin.next;
		}
		return result.toString();
	}
	
}
